// /////////////////////////////////////////////////////////////////////////////
//
// Name:            TerrainComponent.cpp
// Author:          Michael Bartsch (ZCCdark203)
//
// Desc :           Component wrapper of Irrlicht's ITerrainSceneNode.
//
// License:         Copyright (C) 2009 Michael Bartsch and Contributors
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the zlib/libpng License.
//                  See main.cpp for conditions of distribution and use.
//
// /////////////////////////////////////////////////////////////////////////////

// Include files
#include "TerrainComponent.h"
#include "../../core/GameManager.h"


// TerrainComponent class
// TerrainComponent constructor (default).
TerrainComponent::TerrainComponent(Entity *parent)
: SceneComponent(parent, true)
{
    if(pParent != NULL)
      init();
}

// TerrainComponent constructor.
TerrainComponent::TerrainComponent(Entity *parent, const std::string &fileName,
                                   const vector3df &scale, const SColor &vertexColor, s32 maxLOD,
                                   s32 smoothFactor)
: SceneComponent(parent, true)
{
    if(pParent != NULL)
    {
       init(scale, vertexColor, maxLOD, smoothFactor);
       loadHeightMap(fileName.c_str(), vertexColor, smoothFactor);
    }
}

// TerrainComponent deconstructor.
TerrainComponent::~TerrainComponent()
{
    if(pParent != NULL)
    {
       mTerrainSN->remove();
       mSceneNode = NULL;
    }
}

// Common initialisation method.
void TerrainComponent::init(const vector3df &scale, const SColor &vertexColor, s32 maxLOD,
                            s32 smoothFactor)
{
    // Get pointers to needed sub-systems of the Game Manager.
    ISceneManager *pSceneMgr = GameManager::Instance()->getSceneManager();

    // Create the internal scene node.
    mTerrainSN = pSceneMgr->addTerrainSceneNode(0, 0, pParent->getID(),
                                                pParent->getAbsolutePosition(),
                                                pParent->getAbsoluteRotation(), scale, vertexColor,
                                                maxLOD, ETPS_17, smoothFactor, true);
    mSceneNode = mTerrainSN;

    // Set-up internal mechanism for collision detection.
    mTriSelector = pSceneMgr->createTerrainTriangleSelector(mTerrainSN);
    mMetaSelector = pSceneMgr->createMetaTriangleSelector();
}

// Returns a direct pointer to the ITerrainSceneNode.
ITerrainSceneNode* TerrainComponent::getTerrainSceneNode()
{
    return mTerrainSN;
}

// Get the bounding box of the terrain.
const aabbox3df& TerrainComponent::getBoundingBox() const
{
    return mTerrainSN->getBoundingBox();
}

// Gets the bounding box of a patch.
const aabbox3df& TerrainComponent::getBoundingBox(s32 patchX, s32 patchZ) const
{
    return mTerrainSN->getBoundingBox(patchX, patchZ);
}

// Gets the height of a point of the terrain.
f32 TerrainComponent::getHeight(f32 x, f32 y)
{
    return mTerrainSN->getHeight(x, y);
}

// Gets pointer to the mesh.
IMesh* TerrainComponent::getMesh()
{
    return mTerrainSN->getMesh();
}

// Gets center of terrain.
const vector3df& TerrainComponent::getTerrainCenter() const
{
    return mTerrainSN->getTerrainCenter();
}

// Loads the data from a heightMapFile.
bool TerrainComponent::loadHeightMap(const std::string &fileName, const SColor &vertexColor,
                                     s32 smoothFactor)
{
    // Set up variables.
    AssetGroup *assets = pParent->getAssetGroup();
    IReadFile *heightMap = 0;

    // Retrieve pointer to the heightmap.
    if(assets != NULL)
    {
        // Unsubscribe from previous asset.
        assets->disconnectEventSignal(std::string("heightmaps/") + mHeightMapFileName, this);

        // Get heightmap.
        HeightmapProcessor *processor = static_cast<HeightmapProcessor*>(assets->getAssetProcessor("heightmaps"));
        heightMap = processor->getHeightMap(fileName);

        if(heightMap)
        {
           assets->connectEventSignal(std::string("heightmaps/") + fileName, this,
                                        &TerrainComponent::onHeightMap);
           mHeightMapFileName = fileName;
        }
    }

    else
    {
       heightMap = GameManager::Instance()->getDevice()->getFileSystem()
                     ->createAndOpenFile(fileName.c_str());
    }

    // Set heightmap.
    bool result = mTerrainSN->loadHeightMap(heightMap, vertexColor, smoothFactor);
    mVertexColor = vertexColor;
    mSmoothFactor = smoothFactor;

    // Clean up
    heightMap->drop();
    return result;
}

// Scales the base texture.
void TerrainComponent::scaleTexture(f32 scale, f32 scale2)
{
    mTerrainSN->scaleTexture(scale, scale2);
}

// Responds to changes of the heightmap attached to this component.
void TerrainComponent::onHeightMap(void *p)
{
    // Get pointer to the new heightmap.
    IReadFile *heightMap = reinterpret_cast<IReadFile*>(p);

    // Set new heightmap.
    mTerrainSN->loadHeightMap(heightMap, mVertexColor, mSmoothFactor);

    heightMap->drop();
}

// Serializes this object.
bool TerrainComponent::onSerialization(SerializedAttributes *attributes)
{
    // Create a root.
    SerializedAttributes *root = new SerializedAttributes();

    // Serialize SceneComponent elements.
    SceneComponent::onSerialization(root);

    // Serialize attributes.
    root->pushString("heightMap", mHeightMapFileName);

    // Add root to the given collection of attributes.
    attributes->pushObject("TerrainComponent", root);
    root->drop();

    // Return
    return true;
}

// Deserializes this object.
bool TerrainComponent::onDeserialization(SerializedAttributes *attributes)
{
    // Retrieve root attribute from the collection.
    SerializedAttribute *rootAttribute = attributes->pullAttribute();

    if(rootAttribute == NULL || rootAttribute->getName() != "TerrainComponent")
      return false;

    SerializedAttributes *root = reinterpret_cast<SerializedAttributes*>(rootAttribute->getObject());
    root->grab();

    rootAttribute->drop(); // We no longer need this attribute.

    // Process all attributes in the root collection.
    for(SerializedAttribute *attribute = root->pullAttribute(); attribute != NULL; attribute = root->pullAttribute())
    {
       // Deserialize attributes
       if(attribute->getName() == "SceneComponent")
       {
           // Rewrap attribute so that it can be deserialized properly.
           SerializedAttributes *wrappedAttribute = new SerializedAttributes();
           wrappedAttribute->pushAttribute(attribute);

           // Deserialize the component.
           SceneComponent::onDeserialization(wrappedAttribute);

           // Clean up.
           wrappedAttribute->drop();
       }

       else if(attribute->getName() == "heightMap")   loadHeightMap(attribute->getString());

       // We no longer need a reference to the attribute.
       attribute->drop();
    }

    // We no longer need a reference to the root.
    root->drop();

    // We're done.
    return true;
}

// End of File
